/*
 * 对session的处理
 * 最简单的session实现，你可以使用任意自己实现的session管理工具
 * 所以这个session更像是演示之用, 你懂的
 * author:
 */
package illusion

import (
	"sync"
	//	"val/list"
	"errors"
	//"syscall"
	//"fmt"
//	"fmt"
	//"fmt"
	"os"
	"io/ioutil"
	"encoding/json"
	"github.com/go-redis/redis"
)
var (
	errNoSessionImpl = errors.New("传入的session实现为nil")
	errNoStoreFound = errors.New("未找到对应session存储的value值")
)
// 所有session需要实现的接口
type session interface {
	Read(key string) string  // 读取key对应的值
	Store(key,val string)  //存储key => value
	Delete(key string) //删除对应的key对应的值
	Destroy()  // 销毁session
	Save()     //保存一下
	Sid()  string    //获取session id
}

//类似工厂模式里面的工厂
//专门用来生产session的一个函数
type newSessionFunc func(sid string)session

// session管理器，存储对应的session实现
//就像一个工厂一样,完全不像啊
type sessionFactory struct {
	factory  newSessionFunc
	//这里应该是个pool ?
	//instance session
	err      error  //存放错误的地方
}
var sf *sessionFactory
var sfOnce sync.Once

func SessionFactory() *sessionFactory {
	sfOnce.Do(func(){
		sf = &sessionFactory{
			factory:nil,
			err: nil,
		}
	})
	return sf
}

func (sm *sessionFactory) setFactory(factory newSessionFunc) {
	if factory == nil {
		sm.err = errors.New("session生成函数不能为空")
		return
	}
	sm.factory = factory
}

func (sm *sessionFactory)SetSession(_type string) {
	switch _type {
	case SessionOfMemory:
		sm.setFactory(sessionFactoryOfMemory)
		break
	case SessionOfFile:
		sm.setFactory(sessionFactoryOfFile)
		break
	case SessionOfRedis:
		sm.setFactory(sessionFactoryOfRedis)
		break
	default:
		panic("SetSession:未知的session _type")
	}
}

func (sm *sessionFactory)Session(sid string) session{
	//todo buggy
	//if sm.instance != nil && sm.instance.Sid() == sid {

	//	return sm.instance
	//}
	if sm.err != nil || sm.factory == nil{
		logging("获取session失败, session工厂函数为nil或者存在err")
		os.Exit(0)
	}
	return sm.factory(sid)
}

func (sm *sessionFactory)Err() error{
	return sm.err
}

//session接口的内存存储实现
type sessionOfMemory struct {
	sync.Mutex
	Store map[string]map[string]string
	err error
}
var sm *sessionOfMemory
var smOnce sync.Once

func sessionFactoryOfMemory(sid string) session {
	smOnce.Do(func(){
		sm = &sessionOfMemory{
			Store:make(map[string]map[string]string),
			err: nil,
		}
	})
	sm.Lock()
	defer sm.Unlock()
	if s, ok := sm.Store[sid]; ok {
		return &MemorySession{val: s, sid:sid}
	}else {
		s = make(map[string]string)
		sm.Store[sid] = s
		return &MemorySession{val: s, sid:sid}
	}
}

type MemorySession struct {
	//sync.RWMutex  //可同时读取，写锁
	val map[string]string
	sid string
}

func (m *MemorySession)Sid()string{
	return m.sid
}

func (m *MemorySession)Read(key string) string {
	//m.Lock()
	//defer m.Unlock()
	if val, ok := m.val[key]; ok {
		return val
	}else {
		return ""
	}
}

func (m *MemorySession) Store(key, val string) {
	//m.Lock()
	//defer m.Unlock()
	m.val[key] = val
}

func (m *MemorySession) Delete(key string) {
	//m.Lock()
	//defer m.Unlock()
	delete(m.val, key)
}

func (m *MemorySession)Destroy() {
	for key := range m.val { //清空
		delete(m.val, key)
	}
}
func (m *MemorySession)Save(){ //什么都不用干
	return
}

type sessionOfFile struct {
//	sync.RWMutex
	file *os.File
	val map[string]string
	sid string
	err error
}
//var sof *sessionOfFile
//var soOnce sync.Once

//这里有个问题
func sessionFactoryOfFile(sid string) session{
	//if is_debug {
//		Logger().Println("进入构造sessionOfFile步骤("+ sid + ")")
	//}
	//soOnce.Do(func(){
	if sid == "" {
		//怎么办 ??
	}
	sof := &sessionOfFile{
		val: make(map[string]string),
		file: nil,
		err: nil,
		sid:sid,
	}
	return sof
	//})
	//sof.sid = sid
	//每次都要打开一个新的file
	/*file,err := sessionFile(sid)
	if err != nil {
		logging("无法获取session存储文件,错误:" + err.Error())
		os.Exit(0)
	}
	sof.file = file
	bytes,err := ioutil.ReadAll(sof.file)
	if err != nil {
		panic(err.Error())
	}
	if len(bytes) <= 0 { //空文件
		logging("获取session("+ sof.sid + ")值:" + string(bytes[:]))
		return sof
	}
	if is_debug {
		logging("获取session("+ sof.sid + ")值:" + string(bytes[:]))
	}
	err = json.Unmarshal(bytes, sof.val)
	if err != nil {
		logging(err.Error())
		panic(err.Error())
	}
	return sof*/
}

//延迟session文件读取
func (m *sessionOfFile) tryLoadSessionFile(){
	file,err := sessionFile(m.sid)
	if err != nil {
		logging("无法获取session存储文件,错误:" + err.Error())
		os.Exit(0)
	}
	m.file = file
	bytes,err := ioutil.ReadAll(m.file)
	if err != nil {
		panic(err.Error())
	}
	if len(bytes) <= 0 { //空文件
		logging("获取session("+ m.sid + ")值:" + string(bytes[:]))
		return
	}
	if is_debug {
		logging("获取session("+ m.sid + ")值:" + string(bytes[:]))
	}
	err = json.Unmarshal(bytes, m.val)
	if err != nil {
		logging(err.Error())
		panic(err.Error())
	}
	return
}

func (m *sessionOfFile)Sid()string{
	return m.sid
}
func (m *sessionOfFile)Read(key string)string{
//	m.Lock()
	//defer m.Unlock()
	if m.file == nil { //延迟读取
		m.tryLoadSessionFile()
	}
	if val, ok := m.val[key]; ok {
		return val
	}else {
		return ""
	}
}

func (m *sessionOfFile)Store(key,val string) {
	//m.Lock()
	//defer m.Unlock()
	if m.file == nil {
		m.tryLoadSessionFile()
	}
	m.val[key] = val
	x := ""
	if is_debug {
		for key := range m.val {
			x = "&" + x + key +"=" + m.val[key]
		}
		Logger().Println("session(" + m.sid + ")值:" + x)
	}
}
func (m *sessionOfFile)Delete(key string) {
	//m.Lock()
	//defer m.Unlock()
	if m.file == nil {
		m.tryLoadSessionFile()
	}
	delete(m.val, key)
}
func (m *sessionOfFile)Destroy() {
	//m.Lock()
	//defer m.Unlock()
	if m.file == nil {
		m.tryLoadSessionFile()
	}
	for key := range m.val { //清空
		delete(m.val, key)
	}
}
func (m *sessionOfFile)Save(){ //什么都不用干
	//m.Lock()
	//defer m.Unlock()
	if m.file == nil { //这个是有必要的啊
		logging("进入文件session环节")
		return
	}
	if is_debug {
		Logger().Println("进入存储session("+ m.sid + ")步骤")
	}
	bytes, err := json.Marshal(m.val)
	if err != nil {
		panic(err.Error())
	}
	//ioutil.W
	_, err = m.file.Seek(0, 0)
	if err != nil {
		panic(err.Error())
	}
	_,err = m.file.Write(bytes)
	if err != nil {
		panic(err.Error())
	}
	if is_debug {
		v := string(bytes[:])
		Logger().Println("session(" + m.sid + ")存储值:" + v)
	}
	m.file.Close()
	return
}

//也需要做延迟处理
type sessionOfRedis struct {
	//sync.RWMutex
	//client *redis.Client
	val map[string]string
	sid string
	err error
}
var _client *redis.Client
//var sor *sessionOfRedis
var sorOnce sync.Once

func sessionFactoryOfRedis(sid string) session {
	sorOnce.Do(func(){
		//todo 超时问题有待解决
		_client = redisClient()
		_, err := _client.Ping().Result()
		if err !=  nil {
			logging(err.Error())
			panic(err.Error())
		}
	})
	sor := &sessionOfRedis{
		sid: sid,
		val: nil, //可作为标记之用
		err: nil,
	}
	ensureRedisConnect()
	//sor.decodeFromRedis()
	return sor
	//sor.sid = sid
	//做一个ping测试,确保连接通畅
	//_, err := _client.Ping().Result()
	//if err !=  nil {
	//	Logger().Fatalln(err.Error())
	//	if is_debug {
	//		fmt.Println(err.Error())
	//	}
	//	os.Exit(0)
	//}
	//cmd := _client.Get(sid)
	//bytes,err := cmd.Bytes()
	//if err != nil {
	//	panic(err.Error())
	//}
	//json.Unmarshal(bytes, sor.val)
	//return sor
}

func (r *sessionOfRedis)decodeFromRedis() {
	if r.val != nil {
		return
	}
	r.val = make(map[string]string)
	cmd := _client.Get(r.sid)
	bytes,_ := cmd.Bytes()
	if len(bytes) == 0 {
		return
	}
	//if err != nil {
	//	panic(err.Error())
	//}
	json.Unmarshal(bytes, r.val)
}

func ensureRedisConnect() {
	_, err := _client.Ping().Result()
	if err != nil {
		logging("错误:" + err.Error())
		logging("redis连接中断，尝试重新连接")
		_client = redisClient()
		_, err = _client.Ping().Result()
		if err != nil {
			panic(err.Error())
		}
	}
}


func (r *sessionOfRedis)Sid()string{
	return r.sid
}

func (r *sessionOfRedis)Read(key string) string{
	//r.Lock()
	//defer r.Unlock()
	r.decodeFromRedis()
	if value, ok := r.val[key]; ok {
		return value
	}
	return ""
}
func (r *sessionOfRedis)Store(key,val string) {
	//r.Lock()
	//defer r.Unlock()
	r.decodeFromRedis()
	r.val[key] = val
}

func (r *sessionOfRedis)Delete(key string) {
	//r.Lock()
	//defer r.Unlock()
	r.decodeFromRedis()
	delete(r.val, key)
}
//
func(r *sessionOfRedis)Destroy() {
	//r.Lock()
	//defer r.Unlock()
	r.decodeFromRedis()
	for key := range r.val {
		delete(r.val, key)
	}
}

func (r *sessionOfRedis)Save(){
	//r.Lock()
	//defer r.Unlock()
	if r.val == nil { //过滤条件
		return
	}
	bytes, err := json.Marshal(r.val)
	if err != nil {
		panic(err.Error())
	}
	sb := string(bytes[:])
	_client.Set(r.sid, sb, 0)
	return
}